﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Timers;
using TaskSchedulingSystemForm.Model;
using TSSTaskBasicClass;
using System.Threading;
using System.Reflection;
using Quartz;
using System.Diagnostics;
using TaskSchedulingSystemForm.Common;

namespace TaskSchedulingSystemForm.TaskMonitor
{
    class TaskTrigger : MarshalByRefObject
    {
        private TaskConfig configTSS;
        private System.Timers.Timer timerTSS;
        private List<TaskModel> lastStartTaskPool;
        private static readonly object lockDowork = new object();
        /// <summary>
        /// 刷新主界面，使用时需要传入int型，参数解释，1：重新创建左侧任务浏览树，2：当任务执行状态发生改变时刷新任务浏览树的执行状态，3：刷新任务执行进度
        /// </summary>
        public Action<int> FreshenMainForm;   
       /// <summary>
        ///  任务触发器无参构造函数 
       /// </summary>
        public TaskTrigger()
        {
        }
        /// <summary>
        /// 任务触发器构造函数
        /// </summary>
        /// <param name="config"></param>
        public TaskTrigger(TaskConfig config)
        {
            timerTSS = new System.Timers.Timer();
            configTSS = config;
            lastStartTaskPool = new List<TaskModel>();
            InitializeAllTaskCarryTime();
        }
        /// <summary>
        /// 启动任务监视(自动调度任务时启用)
        /// </summary>
        public void Start()
        {
            timerTSS.Interval = GetTimerTSSInterval();//启动 触发器时计算当前应该休眠多长时间
            timerTSS.Elapsed += DoTaskSchelingMethod;
            timerTSS.Start();
        }
        /// <summary>
        /// 初始化或刷新所有任务下一次启动时间
        /// </summary>
        private void InitializeAllTaskCarryTime()
        {
            foreach (var taskType in this.configTSS.allTaskType)
            {
                foreach (var task in taskType.taskLst)
                {
                    DateTimeOffset? t = null;
                    DateTimeOffset utcNow = SystemTime.UtcNow();
                    CronExpression expression = new CronExpression(task.cronExpression);
                    t = expression.GetNextValidTimeAfter(utcNow);
                    task.lastCarryTime = TimeZone.CurrentTimeZone.ToLocalTime(Convert.ToDateTime(t.ToString()));
                }
            }
        }
        /// <summary>
        /// 计算当前 timer需要休眠的时间 并将下次要执行的任务加入到欲执行任务池
        /// </summary>
        /// <returns></returns>
        private int GetTimerTSSInterval()
        {
            int Interval = 0;
            DateTime lastTime = configTSS.allTaskType[0].taskLst[0].lastCarryTime;
            foreach (var taskType in configTSS.allTaskType)
            {
                foreach (var task in taskType.taskLst)
                {
                    if (!task.isRunTask)
                    {                    
                        if (DateTime.Compare(lastTime, task.lastCarryTime) > 0 && DateTime.Compare(DateTime.Now, task.lastCarryTime) < 0)
                        {
                            lastStartTaskPool.Clear();
                            lastStartTaskPool.Add(task);
                            lastTime = task.lastCarryTime;
                        }
                        else if (DateTime.Compare(lastTime, task.lastCarryTime) == 0)
                        {
                            lastStartTaskPool.Add(task);
                        }
                    }
                }
            }
            TimeSpan ts = lastTime.Subtract(DateTime.Now);
            Interval = (ts.Days*24*60*60 + ts.Hours*60*60 +ts.Minutes*60 + ts.Seconds)*1000;//获取时间间隔 并转换成毫秒
            if (Interval <= 0)
            {
                //如果算出的时间小于0 则证明所有任务都在启动中 未结束
                TSSLog.WrriteXTLog(string.Format("异常时间间隔：{0}ms",Interval));
                return 24 * 60 * 60 * 1000;
            }
            else
            {
                TSSLog.WrriteXTLog(string.Format("时间间隔：{0}ms", Interval));
                return Interval;
            }
        }
        /// <summary>
        /// 根据任务线程id计算任务的下一次执行时间
        /// </summary>
        /// <param name="threadId"></param>
        /// <returns></returns>
        private TaskModel GetNextValidCarryTime(int threadId)
        {
            DateTimeOffset? t = null;
            DateTimeOffset utcNow = SystemTime.UtcNow();
            foreach (var taskType in configTSS.allTaskType)
            {
                foreach (var task in taskType.taskLst)
                {
                    if (threadId == task.taskThreadId)
                    {
                        CronExpression expression = new CronExpression(task.cronExpression);
                        t = expression.GetNextValidTimeAfter(utcNow);
                        task.lastCarryTime = TimeZone.CurrentTimeZone.ToLocalTime(Convert.ToDateTime(t.ToString()));
                        task.isRunTask = false;
                        //刷新界面左边任务的执行状态
                        if (FreshenMainForm != null)
                        {
                            FreshenMainForm(2);
                        }
                        task.taskThreadId = 0;
                        return task;
                    }
                }
            }
            return null;
        }
        /// <summary>
        /// 任务调度过程
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void DoTaskSchelingMethod(object sender, ElapsedEventArgs e)
        {
            lock (lockDowork)
            {
                 
                foreach (var task in lastStartTaskPool)
                {
                    if (!task.isRunTask)
                    {
                        try
                        {
                            TSSLog.WrriteXTLog(task, TSSOP.StartTask);
                            task.isRunTask = true;
                            task.taskContext = "";
                            //这里是具体任务入口 bt.DoTaskWork包含了任务的处理逻辑
                            //ABasicTaskWork bt = (ABasicTaskWork)Assembly.LoadFrom(task.taskDllPath).CreateInstance(task.taskClass);
                            //ABasicTaskWork bt = (ABasicTaskWork)task.ass.CreateInstance(task.taskClass);
                            AppDomain ad = AppDomain.CreateDomain(task.taskName);
                            task.ad = ad;
                            ABasicTaskWork bt = (ABasicTaskWork)ad.CreateInstanceFromAndUnwrap(task.taskDllPath, task.taskClass);
                            Thread thread = new Thread(new ParameterizedThreadStart(bt.DoTaskWork));
                            bt.endOfTaskOP = EndOfTask;
                            bt.printfMessageOP = PrintfMessage;
                            bt.getTaskProgressOP = GetTaskProgress;
                            thread.Name = task.taskName;
                            //保存任务线程的id 并将该任务类实例对象传入任务逻辑 以方便任务执行完毕返回结果时调度系统将结果分配到该任务
                            task.taskThreadId = thread.ManagedThreadId;
                            bt.threadId = task.taskThreadId;
                            task.isRunTask = true;
                            if (FreshenMainForm != null)
                            {
                                FreshenMainForm(2);
                            }
                            MainForm.CarryNowTaskIdPool.Add(task.taskThreadId, false);
                            MainForm.taskCarryNowLst.Add(task);
                            MainForm.taskWorkObjectPool.Add(task.taskThreadId, bt);
                            //
                            thread.Start(task);
                        }
                        catch(Exception ex)
                        {
                            task.isRunTask = false;
                            TSSLog.WrriteXTLog(task, TSSOP.StartTaskFaild);
                            TSSLog.WrriteXTLog(ex.StackTrace.ToString());
                            Mail.SendMail(TSSOP.StartTaskFaild, ex.StackTrace.ToString(), task);
                        }
                    }
                }
                //本次扫描任务执行池后将任务池清空  异防止同一任务呗多次启动
                lastStartTaskPool.Clear();
                //为防止当前启动的任务执行时间太长 timer的休眠时间长时间不修改导致丢失任务  所以本次任务调度完之后立即查询得到下一次要启动的任务
                timerTSS.Interval = GetTimerTSSInterval();
            }
        }
        /// <summary>
        /// 手动调度室执行
        /// </summary>
        /// <param name="task"></param>
        public void DoTaskSchelingMethodByhand(TaskModel task)
        {
            if (!task.isRunTask)
            {
                try
                {
                    TSSLog.WrriteXTLog(task, TSSOP.StartTaskByHand);
                    task.isRunTask = true;
                    task.taskContext = "";
                    //这里是具体任务入口 bt.DoTaskWork包含了任务的处理逻辑
                    //ABasicTaskWork bt = (ABasicTaskWork)Assembly.LoadFrom(task.taskDllPath).CreateInstance(task.taskClass);
                    //ABasicTaskWork bt = (ABasicTaskWork)task.ass.CreateInstance(task.taskClass);
                    AppDomain ad = AppDomain.CreateDomain(task.taskName);
                    task.ad = ad;
                    ABasicTaskWork bt = (ABasicTaskWork)ad.CreateInstanceFromAndUnwrap(task.taskDllPath, task.taskClass);
                    Thread thread = new Thread(new ParameterizedThreadStart(bt.DoTaskWork));
                    bt.endOfTaskOP = EndOfTask;
                    bt.printfMessageOP = PrintfMessage;
                    bt.getTaskProgressOP = GetTaskProgress;
                    thread.Name = task.taskName;
                    //保存任务线程的id 并将该任务类实例对象传入任务逻辑 以方便任务执行完毕返回结果时调度系统将结果分配到该任务
                    task.taskThreadId = thread.ManagedThreadId;
                    bt.threadId = task.taskThreadId;
                    task.isRunTask = true;
                    if (FreshenMainForm != null)
                    {
                        FreshenMainForm(2);
                    }
                    
                    MainForm.CarryNowTaskIdPool.Add(task.taskThreadId, false);
                    MainForm.taskCarryNowLst.Add(task);
                    MainForm.taskWorkObjectPool.Add(task.taskThreadId, bt);
                    //
                    thread.Start(task);
                }
                catch (Exception ex)
                {
                    task.isRunTask = false;
                    TSSLog.WrriteXTLog(task, TSSOP.StartTaskByHandFaild);
                    TSSLog.WrriteXTLog(ex.StackTrace.ToString());
                    TSSLog.WrriteXTLog(ex.Message.ToString());
                }
            }
        }
        /// <summary>
        /// 启动任务线程 合并任务程序集时使用此方法调度
        /// </summary>
        /// <param name="task"></param> 
        private void StartTaskThread(object ta)
        {
            TaskModel task = ta as TaskModel;
            Action<int> parameter1 = EndOfTask;
            Action<string, int> parameter2 = PrintfMessage;
            Action<int, int> parameter3 = GetTaskProgress;
            object[] args = new object[3];
            args[0] = parameter1;
            args[1] = parameter2;
            args[2] = parameter3;
            Assembly ass = Assembly.LoadFrom(task.taskDllPath);
            Type t = ass.GetType(task.taskClass);

            MethodInfo info = t.GetMethod("DoTaskWork");
            PropertyInfo[] propertyinfo = t.GetProperties();
            object ob = Activator.CreateInstance(t, args);
            FieldInfo fieldInfo = t.GetField("threadId", BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance
                                    | BindingFlags.Static);
            fieldInfo.SetValue(ob, task.taskThreadId);
            info.Invoke(ob, new object[] { 0 });
        }
        /// <summary>
        /// 任务自信完后要执行的操作
        /// </summary>
        /// <param name="threadId"></param>
        private void EndOfTask(int threadId)
        {
            //AppDomain ad = AppDomain.CurrentDomain;
            //string name = ad.FriendlyName;
            //Assembly[] a = ad.GetAssemblies();
            //foreach(var aa in a)
            //{
            //    TSSLog.WrriteXTLog(aa.FullName);
            //}
            TSSLog.WrriteXTLog(FindTaskObjectByThreadId(threadId), TSSOP.EndOfTask);
            Thread.Sleep(2000);
            //给刚结束的任务生成下一次启动时间
            TaskModel task = GetNextValidCarryTime(threadId);        
            //计算timer的新的休眠时间 并设置
            timerTSS.Interval = GetTimerTSSInterval();
            //该任务已经执行结束  线程id池中该任务信息必须删除
            MainForm.CarryNowTaskIdPool.Remove(threadId);
            //正在执行任务池也酱紫清除
            MainForm.taskCarryNowLst.Remove(task);
            //任务类对象实例移除
            MainForm.taskWorkObjectPool.Remove(threadId);
            //卸载该任务执行时加载的程序集，结束任务
            AppDomain.Unload(task.ad);
        }
        /// <summary>
        /// 打印任务输出的消息
        /// </summary>
        /// <param name="message"></param>
        /// <param name="threadId"></param>
        private void PrintfMessage(string message, int threadId)
        {
            DIYConsole.WriteLine(message, threadId);
            TSSLog.WrriteTaskLog(message,FindTaskObjectByThreadId(threadId));
        }
        /// <summary>
        /// 获取任务的执行进度
        /// </summary>
        /// <param name="progress"></param>
        /// <param name="threadId"></param>
        private void GetTaskProgress(int progress, int threadId)
        {
            TaskModel task = FindTaskObjectByThreadId(threadId);
            task.taskProcess = progress;
            if (FreshenMainForm != null)
            {
                FreshenMainForm(3);
            }
        }
        /// <summary>
        /// 根据线程id查找任务对象
        /// </summary>
        /// <param name="threadId"></param>
        /// <returns></returns>
        private TaskModel FindTaskObjectByThreadId(int threadId)
        {
            foreach (var taskType in configTSS.allTaskType)
            {
                foreach (var task in taskType.taskLst)
                {
                    if (threadId == task.taskThreadId)
                    {
                        return task;
                    }
                }
            }
            return null;
        }    
    }
}
